import json

class TopoQuery:

	def __init__(self, topo_desc):
		with open(topo_desc) as data_file:
			self.data = json.load(data_file)

	def get_switch_type(self, datapath_id):
		datapath_id = str(datapath_id)
		return self.data["switch"][datapath_id]["switch-type"]

	def get_next_hop_id(self, datapath_id, port_no):
		datapath_id = str(datapath_id)
		port_no = str(port_no)
		return self.data["switch"][datapath_id]["ports"][port_no][0]

	def get_link_id(self, datapath_id, port_no):
		datapath_id = str(datapath_id)
		port_no = str(port_no)
		return self.data["switch"][datapath_id]["ports"][port_no][1]

	def get_host_link_id(self, host_mac):
		host_mac = str(host_mac)
		return self.data["host_link"][host_mac]

	def get_pod_id(self, mac_or_dpid):
		mac_or_dpid = str(mac_or_dpid)
		if mac_or_dpid in self.data["pod"]:
			return self.data["pod"][mac_or_dpid]

	# 1. get_near_link
	# 2. build_graph
	# 3. get_path

	# link_id ==> int
	def get_near_link(self, link_id):
		ret = set()
		for dpid, switch in self.data['switch'].items():
			is_find = False
			for port_no, port in switch['ports'].items():
				if len(port) == 2 and int(port[1]) == link_id:
					is_find = True
					break
			if is_find:
				for port_no, port in switch['ports'].items():
					if len(port) == 2 and int(port[1]) != link_id:
						ret.add(port[1])
		return list(ret)


	def build_graph(self):
		graph = {}
		for dpid, switch in self.data['switch'].items():
			for port_no, port in switch['ports'].items():
				if len(port) == 1:
					continue
				graph[port[1]] = {
					"visit":False,
					"next":self.get_near_link(int(port[1]))
				}
		return graph

	# link_src, link_dest ==> str
	def get_path(self, link_src, link_dest):
		queue = []
		queue.append(link_src)
		prev = {}
		prev[link_src] = None
		graph = self.build_graph()
		#print graph
		graph[link_src]['visit'] = True
		is_find = False
		for link in queue:
			#print link
			for near_link in graph[link]['next']:
				if graph[near_link]['visit'] == False and prev[link] != near_link:
					graph[near_link]['visit'] = True
					prev[near_link] = link
					queue.append(near_link)
				if near_link == link_dest:
					is_find = True
					break
			if is_find:
				break

		path = []
		link = link_dest
		while link is not None:
			path.insert(0, prev[link])
			link = prev[link]
		return list(map(lambda x: int(x), path[2:]))

